package com.tencent.cloud.tuikit.tuicall_engine;

import android.content.Context;

import androidx.annotation.NonNull;

import com.tencent.cloud.tuikit.tuicall_engine.utils.Logger;
import com.tencent.qcloud.tuikit.TUICommonDefine;
import com.tencent.qcloud.tuikit.tuicallengine.TUICallDefine;
import com.tencent.qcloud.tuikit.tuicallengine.TUICallEngine;
import com.tencent.qcloud.tuikit.tuicallengine.TUICallObserver;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;

/**
 * TUICallEnginePlugin
 */
public class TUICallEnginePlugin implements FlutterPlugin, MethodCallHandler {
    private static final String TAG = "TUICallEnginePlugin";

    private MethodChannel channel;
    private Context       mApplicationContext;

    @Override
    public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
        channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "tuicall_engine");
        channel.setMethodCallHandler(this);

        mApplicationContext = flutterPluginBinding.getApplicationContext();
    }

    @Override
    public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
        Logger.info(TAG, "onMethodCall -> method:" + call.method + ", arguments:" + call.arguments);
        try {
            Method method = TUICallEnginePlugin.class.getDeclaredMethod(call.method, MethodCall.class,
                    MethodChannel.Result.class);
            method.invoke(this, call, result);
        } catch (NoSuchMethodException e) {
            Logger.error(TAG, "|method=" + call.method + "|arguments=" + call.arguments + "|error=" + e);
            result.notImplemented();
        } catch (IllegalAccessException e) {
            Logger.error(TAG, "|method=" + call.method + "|arguments=" + call.arguments + "|error=" + e);
        } catch (Exception e) {
            Logger.error(TAG, "|method=" + call.method + "|arguments=" + call.arguments + "|error=" + e);

        }
    }

    @Override
    public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
        channel.setMethodCallHandler(null);
    }


    public void addObserver(MethodCall call, MethodChannel.Result result) {
        TUICallEngine.createInstance(mApplicationContext).addObserver(mTUICallObserver);
        result.success("");
    }

    public void removeObserver(MethodCall call, MethodChannel.Result result) {
        TUICallEngine.createInstance(mApplicationContext).removeObserver(mTUICallObserver);
        result.success("");
    }

    private TUICallObserver mTUICallObserver = new TUICallObserver() {
        @Override
        public void onError(int code, String message) {
            invokeObserverMethod("onError", new HashMap<String, Object>() {{
                put("code", code);
                put("message", message);
            }});
        }

        @Override
        public void onCallReceived(String callerId, List<String> calleeIdList, String groupId,
                                   TUICallDefine.MediaType callMediaType) {
            invokeObserverMethod("onCallReceived", new HashMap<String, Object>() {{
                put("callerId", callerId);
                put("calleeIdList", calleeIdList);
                put("groupId", groupId);
                put("callMediaType", callMediaType.ordinal());
            }});
        }

        @Override
        public void onCallCancelled(String callerId) {
            invokeObserverMethod("onCallCancelled", new HashMap<String, Object>() {{
                put("callerId", callerId);
            }});
        }

        @Override
        public void onCallBegin(TUICommonDefine.RoomId roomId, TUICallDefine.MediaType callMediaType,
                                TUICallDefine.Role callRole) {
            Map roomIdMap = new HashMap() {
                {
                    put("intRoomId", roomId.intRoomId);

                }
            };
            invokeObserverMethod("onCallBegin", new HashMap<String, Object>() {{
                put("roomId", roomIdMap);
                put("callMediaType", callMediaType.ordinal());
                put("callRole", callRole.ordinal());
            }});
        }

        @Override
        public void onCallEnd(TUICommonDefine.RoomId roomId, TUICallDefine.MediaType callMediaType,
                              TUICallDefine.Role callRole, long totalTime) {

            Map roomIdMap = new HashMap() {
                {
                    put("intRoomId", roomId.intRoomId);

                }
            };
            invokeObserverMethod("onCallEnd", new HashMap<String, Object>() {{
                put("roomId", roomIdMap);
                put("callMediaType", callMediaType.ordinal());
                put("callRole", callRole.ordinal());
                put("totalTime", (double) totalTime);
            }});
        }

        @Override
        public void onCallMediaTypeChanged(TUICallDefine.MediaType oldCallMediaType,
                                           TUICallDefine.MediaType newCallMediaType) {
            invokeObserverMethod("onCallMediaTypeChanged", new HashMap<String, Object>() {{
                put("oldCallMediaType", oldCallMediaType.ordinal());
                put("newCallMediaType", newCallMediaType.ordinal());
            }});
        }

        @Override
        public void onUserReject(String userId) {
            invokeObserverMethod("onUserReject", new HashMap<String, Object>() {{
                put("userId", userId);
            }});
        }

        @Override
        public void onUserNoResponse(String userId) {
            invokeObserverMethod("onUserNoResponse", new HashMap<String, Object>() {{
                put("userId", userId);
            }});
        }

        @Override
        public void onUserLineBusy(String userId) {
            invokeObserverMethod("onUserLineBusy", new HashMap<String, Object>() {{
                put("userId", userId);
            }});
        }

        @Override
        public void onUserJoin(String userId) {
            invokeObserverMethod("onUserJoin", new HashMap<String, Object>() {{
                put("userId", userId);
            }});
        }

        @Override
        public void onUserLeave(String userId) {
            invokeObserverMethod("onUserLeave", new HashMap<String, Object>() {{
                put("userId", userId);
            }});
        }

        @Override
        public void onUserVideoAvailable(String userId, boolean isVideoAvailable) {
            invokeObserverMethod("onUserVideoAvailable", new HashMap<String, Object>() {{
                put("userId", userId);
                put("onUserVideoAvailable", isVideoAvailable);
            }});
        }

        @Override
        public void onUserAudioAvailable(String userId, boolean isAudioAvailable) {
            invokeObserverMethod("onUserAudioAvailable", new HashMap<String, Object>() {{
                put("userId", userId);
                put("isAudioAvailable", isAudioAvailable);
            }});
        }

        @Override
        public void onUserVoiceVolumeChanged(Map<String, Integer> volumeMap) {
            invokeObserverMethod("onUserVoiceVolumeChanged", new HashMap<String, Object>() {{
                put("volumeMap", volumeMap);
            }});
        }

        @Override
        public void onUserNetworkQualityChanged(List<TUICommonDefine.NetworkQualityInfo> networkQualityList) {

            List<Map> networkQualityMapList = new ArrayList<>();
            for (int i = 0; i < networkQualityList.size(); i++) {
                TUICommonDefine.NetworkQualityInfo networkQualityItem = networkQualityList.get(i);

                networkQualityMapList.add(new HashMap<String, Object>() {{
                    put("userId", networkQualityItem.userId);
                    put("networkQuality", networkQualityItem.quality.ordinal());
                }});

            }

            invokeObserverMethod("onUserNetworkQualityChanged", new HashMap<String, Object>() {{
                put("networkQualityList", networkQualityMapList);
            }});
        }

        @Override
        public void onKickedOffline() {
            invokeObserverMethod("onKickedOffline", new HashMap<String, Object>() {{
            }});
        }

        @Override
        public void onUserSigExpired() {
            invokeObserverMethod("onUserSigExpired", new HashMap<String, Object>() {{
            }});
        }
    };

    private void invokeObserverMethod(String method, Map params) {
        Logger.info(TAG, "invokeObserverMethod method: " + method + " params: " + params);
        Map arguments = new HashMap();
        arguments.put("method", method);
        if (params != null && params.size() != 0) {
            arguments.put("params", params);
        }
        channel.invokeMethod("onTUICallObserver", arguments);
    }
}
